#pragma once
#include <iostream>
#include <string>
#include <set>
#include <cstring>
#include <unistd.h>
#include "Log.hpp"
#include "sys/epoll.h"

std::string EventsToStr(uint32_t events)
{
    string ret = "Events: ";
    if (events & EPOLLIN)
        ret += "EPOLLIN";
    if (events & EPOLLOUT)
        ret += "EPOLLOUT";
    if (events & EPOLLET)
        ret += "EPOLLET";

    return ret;
}

class Epoller
{
    static const int defaultsize = 128;

public:
    Epoller(int epfd = -1) : _epfd(epfd)
    {
    }

    // 创建epoll模型
    void Init()
    {
        int n = epoll_create(defaultsize);
        if (n == -1)
        {
            LOG(Abnormal, "Init epoll model error: %s, %d\n", strerror(errno), errno);
            exit(4);
        }
        _epfd = n;
        LOG(Info, "Init epoll model success, epfd: %d\n", n);
    }

    // 添加需要被关心事件的文件描述符
    void Add(int fd, uint32_t events)
    {
        struct epoll_event ev;
        ev.events = events;
        ev.data.fd = fd;
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &ev);
        if (n == -1)
        {
            LOG(Error, "add epoll sockfd error: %s, %d\n", strerror(errno), errno);
            return;
        }
        _fds.insert(fd);
        LOG(Info, "add epoll sockfd success, fd: %d, events: %s\n", fd, CSTR(EventsToStr(events)));
    }

    void Del(int fd)
    {
        int n = epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
        if (n == -1)
        {
            LOG(Error, "delete epoll sockfd error: %s, %d\n", strerror(errno), errno);
            return;
        }
        _fds.erase(fd);
        LOG(Info, "delete epoll sockfd success, fd: %d, events: %s\n", fd);
    }

    int Wait(epoll_event *ev, int maxevents)
    {
        PrintDebug();
        int timeout = 1000;
        int n = epoll_wait(_epfd, ev, maxevents, timeout);
        return n;
    }

    void PrintDebug()
    {
        cout << "Concerned file descriptors: ";
        for (auto e : _fds)
        {
            cout << e << " ";
        }
        cout << endl;
    }

    ~Epoller() {}

private:
    int _epfd;

    std::set<int> _fds;
};